from __future__ import with_statement
import datetime
from itertools import chain

import clevercss
import paste.translogger
from beaker.middleware import SessionMiddleware, CacheMiddleware
from paste.cascade import Cascade
from paste.urlparser import StaticURLParser
from weberror.errormiddleware import make_error_middleware
from weberror.evalexception import EvalException
from repoze.tm import make_tm

from clutch.globals import G
from clutch import database as db
from clutch import exceptions as exc
from clutch import view as v

# Paste entry points

def make_session_middleware(app, global_config, **local_config):
    return session(app, global_config, **local_config)

def make_cache_middleware(app, global_config, **local_config):
    return cache(app, global_config, **local_config)

def make_transaction_middleware(app, global_config, **local_config):
    return transaction(app, global_config, **local_config)

def make_http_error_middleware(app, global_config, **local_config):
    return HTTPErrorMiddleware(app)

def make_dbsession_middleware(app, global_config, **local_config):
    return dbsession(app, global_config, **local_config)

def make_errordoc_middleware(app, global_config, **local_config):
    return StatusCodeRedirect(app, global_config, **local_config)

def null(app, global_config, **local_config):
    return app

class dbsession(object):

    def __init__(self, app, global_config, **local_config):
        self.app = app

    def __call__(self, environ, start_response):
        environ['clutch.sa_sessions'] = set()
        try:
            for r in self.app(environ, start_response):
                yield r
        finally:
            sessions = environ.pop('clutch.sa_sessions')
            for s in sessions:
                s.remove()

class transaction(object):

    def __init__(self, app, global_config, **local_config):
        self.app = app
        self.mw = make_tm(app, self.commit_veto)

    def __call__(self, environ, start_response):
        return self.mw(environ, start_response)

    def commit_veto(self, environ, status, headers):
        return not (200 <= int(status.split()[0]) < 400)

def session(app, global_config, **local_config):
    local_config = dict((k, eval(v)) for k,v in local_config.iteritems())
    return SessionMiddleware(app, global_config, **local_config)
        
def cache(app, global_config, **local_config):
    return CacheMiddleware(app, global_config, **local_config)

def error(app, global_config, **local_config):
    # return make_error_middleware(app, global_config, **local_config)
    return EvalException(app, global_config, **local_config)

class HTTPErrorMiddleware(object):

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        try:
            return self.app(environ, start_response)
        except exc.WSGIHTTPException, ex:
            if 200 <= ex.code < 500:
                return ex(environ, start_response)
            else:
                raise

class DevelopmentStaticFiles(object):

    def __init__(self, clutch_app, view_config):
        self.clutch_app = clutch_app
        self.app_cfg = clutch_app.app_config
        self.view_cfg = view_config

    def __call__(self, environ, start_response):
        path = environ['PATH_INFO']
        if path.endswith('/'):
            path = path + 'index.html'
        return self.serve_path(path, start_response)

    def serve_path(self, path, start_response):
        # Serve the static public file if it exists
        if self.app_cfg.public.resource_exists(path):
            result = self.app_cfg.public.resource_stream(path)
            start_response('200 OK', [])
            return result 
        # Serve the template if it exists
        tpl = self.find_template(path)
        if tpl:
            with self.clutch_app.context():
                result = list(tpl.render({}))
            start_response('200 OK', [])
            return result
        # Serve clevercss as css
        if path.endswith('.css'):
            clever_fn = path[:-3] + 'clever'
            clever_tpl = self.find_template(clever_fn)
            if clever_tpl:
                with self.clutch_app.context():
                    clever_text = '\n'.join(clever_tpl.render({}))
                css_text = clevercss.convert(clever_text)
                start_response('200 OK', [])
                return [ css_text ]
        raise exc.HTTPNotFound, path

    def find_template(self, path):
        if self.app_cfg.static_templates.resource_exists(path):
            tpl_fn = path
            if path.endswith('.html'):
                engine = self.view_cfg.default_html_engine
            else:
                engine = self.view_cfg.default_text_engine
        else:
            dirname, basename = path.rsplit('/', 1)
            if not self.app_cfg.static_templates.resource_exists(dirname):
                return None
            if not self.app_cfg.static_templates.resource_isdir(dirname):
                return None
            for fn in self.app_cfg.static_templates.resource_listdir(dirname):
                if ':' in fn:
                    engine, tpl_fn = fn.split(':', 1)
                    if tpl_fn == basename: break
            else:
                return None
        # tpl_text = self.app_cfg.static_templates.resource_string(tpl_fn)
        with self.clutch_app.context():
            engine = v.engines.get(engine, path)
            loader = engine.loader(
                self.app_cfg.package_name,
                self.app_cfg.static_templates.objname)
            return loader.template(tpl_fn[1:])

def development_cascade(app, app_config, view_config):
    return Cascade([DevelopmentStaticFiles(app_config, view_config), app])
    


class StatusCodeRedirect(object):

    def __init__(self, app, global_config, **local_config):
        self.app = app
        self.status_map = local_config

    def __call__(self, environ, start_response):
        return StatusCodeRedirectRequest(self, environ, start_response)

class StatusCodeRedirectRequest(object):

    def __init__(self, redirect, environ, start_response):
        self._redirect = redirect
        self._environ = environ
        self._old_start_response = start_response
        self._url = None
        self._sr_args = None

    def start_response(self, status, headers, exc_info=None):
        code, msg = status.split(' ', 1)
        self._url = self._redirect.status_map.get(code, None)
        if self._url is None:
            self._old_start_response(status, headers, exc_info)
        else:
            self._sr_args = status,headers,exc_info

    def __iter__(self):
        result = self._redirect.app(self._environ, self.start_response)
        chunk = result.next()
        if self._url:
            self._environ['PATH_INFO'] = self._url
            return iter(self._redirect.app(self._environ, self._old_start_response))
        else:
            return chain([chunk], result)

    

